<?php
////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//         佛祖保佑       永无BUG     永不修改                     //
////////////////////////////////////////////////////////////////////
/**
 * 自动生成代码
 */

namespace app\htsoft\controller;

use think\Config;
use think\Controller;
use think\Loader;
use think\Request;
use think\Url;
use think\Db;

class Generate extends \app\common\controller\Base
{
    /**
     * Enum类型识别为单选框的结尾字符,默认会识别为单选下拉列表
     */
    protected $enumRadioSuffix = ['data', 'state', 'status'];

    /**
     * var bool 是否覆盖写入
     */
    protected $overwrite = false;

    /**
     * var bool 是否开启文件锁
     */
    protected $lock = true;
    /**
     * Set类型识别为复选框的结尾字符,默认会识别为多选下拉列表
     */
    protected $setCheckboxSuffix = ['data', 'state', 'status'];

    /**
     * Int类型识别为日期时间的结尾字符,默认会识别为日期文本框
     */
    protected $intDateSuffix = ['time', 'date', 'datetime'];

    /**
     * 开关后缀
     */
    protected $switchSuffix = ['switch'];


    /**
     * 识别为图片字段
     */
    protected $imageField = ['image', 'images', 'avatar', 'avatars'];

    /**
     * 识别为富文本编辑器字段
     */
    protected $editorField = ['content', 'description'];

    /**
     * 识别为color类型
     */
    protected $colorField = ['color'];

    /**
     * 识别为文件字段
     */
    protected $fileField = ['file', 'files'];

    /**
     * 保留字段 不会生成对应控件
     */
    protected $reservedField = ['createtime', 'updatetime'];
    /**
     * 其他表主键
     */
    protected $otherTableId = ['_id'];
    public function index()
    {

        $post = Request::instance()->param();
//        dump(APP_PATH);die;
        $tables = Db::query("show table status");
        $this->assign('tables', $tables);
        if (IS_POST) {
            $table = $post['table'];
            $info = $post['info'];
            $add = $post['add'];
            $edit = $post['edit'];
            $delect = $post['delect'];
            $controller = $post['controlname'];
            $title = $post['controltitle'];
            $db_name = C('database.database');
            $prefix = config('database.prefix');
            $where = $post['where']?$post['where']:[];
            $comment = $post['comment'];
            $sql = "SELECT  * FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = '{$db_name}' AND table_name = '{$table}' ORDER BY ORDINAL_POSITION";
            $tabinfo1 = Db::query($sql);
            $tabinfo = [];
            foreach ($tabinfo1 as &$v) {
                $tem = [];
                foreach ($v as $kk => $vv) {
                    $tem[strtolower($kk)] = $vv;

                }
                $tabinfo[] = $tem;
            }
            if (empty($tabinfo1)) {
                $this->error('表 ' . $table . '不存在');
            }
            //先判断主键
            $pri = '';
            $foreignKeyList=[];//外联表 list
            foreach ($tabinfo as $k => $v) {
                if ('PRI' == $v['column_key']) {
                    $pri = $v['column_name'];
                }
                //判断注释是否有效
                $type = $this->getFieldType($v);
//                if (!$this->judgeComment($type, $v['column_name'],$v['column_comment'],$errinfo)) {
//                    $this->error('列：'.$v['column_name'].'注释无效 错误：' .$errinfo);
//                }
                $a44[]=$v;
                if ('id' === $type) {
                    $foreignKeyList[] = $v;

                }
            }
            if (empty($pri)) {
                $this->error('该表无主键');
            }

            try {
                //todo 创建控制器
                $ret=[];
                $ret[] = $this->CreateController($table, $prefix, $controller, $where, $pri,$foreignKeyList);
               //todo 创建列表页
                $controller=strtolower($controller);
                $ret[] = $this->CreateViewlist($tabinfo, $controller, $info, $add, $edit, $delect, $where, $pri);
                $ruleAlert = "规则：<br>".strtolower("Htsoft/$controller/$controller" . 'index')."<br>";
                Db::startTrans();
                $menu=Db::name("menu")->insert(['app' => 'Htsoft', 'model' => $controller, 'action' => $controller . 'list', 'name' => $title . "列表",'status'=>1]);
                $ruleparent=$ruRes[]= Db::name("auth_rule")->insertGetId(["name" => strtolower("Htsoft/$controller/$controller" . 'list'), "module" => 'Htsoft', "type" => "admin_url", "title" => $title . "列表"]);
                if ($info) {
                    //todo 创建查询
                    $ret[] = $this->CreateViewinfo($tabinfo, $controller);
                    $ruRes[]=Db::name("auth_rule")->insert(["name" => strtolower("Htsoft/$controller/$controller" . 'info'), "module" => 'Htsoft', "type" => "admin_url", "title" => $title . "查看","parentid"=>$ruleparent]);
                    $ruleAlert .= strtolower("Htsoft/$controller/$controller" . 'info') . "<br>";
                }
                if ($add) {
                    //todo 创建添加
                    $ret[] = $this->CreateViewadd($tabinfo, $controller);
                    $ruRes[]=Db::name("auth_rule")->insert(["name" => strtolower("Htsoft/$controller/$controller" . 'add'), "module" => 'Htsoft', "type" => "admin_url", "title" => $title . "添加","parentid"=>$ruleparent]);
                    $ruleAlert .= strtolower("Htsoft/$controller/$controller" . 'add') . "<br>";
                }
                if ($edit) {
                    //todo 创建编辑
                    $ret[] = $this->CreateViewedit($tabinfo, $controller);
                    $ruRes[]=Db::name("auth_rule")->insert(["name" => strtolower("Htsoft/$controller/$controller" . 'edit'), "module" => 'Htsoft', "type" => "admin_url", "title" => $title . "编辑","parentid"=>$ruleparent]);
                    $ruleAlert .= strtolower("Htsoft/$controller/$controller" . 'edit') . "<br>";
                }
                if ($delect) {
                    //todo 创建删除
                    $ruRes[]=Db::name("auth_rule")->insert(["name" => strtolower("Htsoft/$controller/$controller" . 'delect'), "module" => 'Htsoft', "type" => "admin_url", "title" => $title . "删除","parentid"=>$ruleparent]);
                }
            } catch (\Exception $e) {
                $this->error($e->getMessage());
            }
            $info = '';
            $num = 0;
            foreach ($ret as $k => $v) {
                if ($v['success']) {
                    $info .= "<span style='color:green'>成功</span> {$v['file']} <br>";
                } else {
                    $info .= "<span style='color:red'>失败</span> {$v['file']} <br>";
                }
            }
            if($menu == false){
                Db::rollback();
                $menuAlert = "{$title}菜单创建<span class='layui-red'>失败</span><br>";
            }else{
                $menuAlert = "{$title}菜单创建<span class='layui-blue'>成功</span><br>";
            }
            if (in_array(false, $ruRes, true)) {
                Db::rollback();
                $ruAlert = "规则创建<span class='layui-red'>失败</span>请手动创建";
            } else {
                Db::commit();
                $ruAlert = "规则创建<span class='layui-blue'>成功</span><br>";
            }
            $info .= $menuAlert;
            $info .= $ruleAlert .$ruAlert.'请自行创建菜单、绑定规则，以及角色组授权';
            $this->success($info);
        }
        return $this->fetch();
    }



    /**创建控制器
     * @param $table数据库表名称
     * @param $prefix数据库表前缀
     * @param $controller控制器名称
     * @param $where查询条件
     * @param $pri数据库表主键
     * @param array $foreignKeyList 外联键列表
     * @return array
     */
    function CreateController($table, $prefix, $controller, $where =array(), $pri, $foreignKeyList = [])
    {
        $table_name = preg_replace('/^(' . $prefix . ')/', '', $table);
        $whereStr = "[";
        if (is_array($where)) {
            foreach ($where as $k => $v) {
                $whereStr .= "'{$k}' => '{$v}',";
            }
        }
        $whereStr = trim($whereStr, ',');
        $whereStr .= ']';
        $date = date('Y-m-d');
        $time = date('H:i');
        $temp = file_get_contents(APP_PATH . 'common' . DS . 'controller' . DS . 'commoncontroller.php');
        $replace_list = array(
            'date' => $date,
            'time' => $time,
            'where' => $whereStr,
            'Controller1' => ucfirst($controller),
            'controller' => strtolower($controller),
            'table' => $table_name,
            'id' => $pri,//开关使用
            "filed"=>"",
            "join"=>"",
            "fktList"=>""
        );
        if ($foreignKeyList) {
            $fList = $this->dealForeignKeyList($foreignKeyList);
            $fktList = '';
            $filed = "->alias('a')->field('a.*";//数据列
            $join = "";//join字符串
            $joinType = 'left';
            if (is_array($fList)) {
                foreach ($fList as $k => $v) {
                    $filed .= ",{$v['tableAlias']}.`{$v['show']}` as {$v['table']}_{$v['show']}";
                    $join .= "->join(DB_PREFIX.'{$v['table']} {$v['tableAlias']}','{$v['tableAlias']}.{$v['pri']}=a.{$k}','{$joinType}')" . PHP_EOL;
                    $fktList .= "\${$v['table']}List = Db::name('{$v['table']}')->field('{$v['pri']},{$v['show']}')->select();" . PHP_EOL;
                    $fktList .= "\$this->assign('{$v['table']}List',\${$v['table']}List);" . PHP_EOL;
                }
            }
            $filed .= "')" . PHP_EOL;
            $replace_list["filed"]=$filed;
            $replace_list["join"]=$join;
            $replace_list["fktList"]=$fktList;
        }
        if (is_array($replace_list)) {
            foreach ($replace_list as $k => $v) {
                $temp = str_replace("{%{{$k}}%}", $v, $temp);
            }
        }
        $path = dirname(__FILE__) . DS . ucfirst($controller) . '.php';
        return $this->writeFile($path, $temp);
    }

    /**
     * 创建视图列表
     * @param $table数据库表名称
     * @param $prefix数据库表前缀
     * 功能参数：
     * @param bool $info 查询
     * @param bool $add 添加
     * @param bool $edit 编辑
     * @param bool $delect 删除
     * @param array $where 查询条件
     * @param $pri 主键字段
     * @return bool|string
     */
    function CreateViewlist($tableinfo, $controller, $info = false, $add = false, $edit = false, $delect = false, $where1 = [], $pri)
    {
        $replace_list = array(
            'controller' => '',
            'fields' => '',
            'where' => '',
            'add' => 'layui-hide',//隐藏按钮
            'edit' => 'layui-hide',
            'delete' => 'layui-hide',
            'info' => 'layui-hide',
            'id' => $pri,
        );
        $temp = $this->gettemp('index');
        $where = '';
        $fields = "{type: \"checkbox\", width: 50}," . PHP_EOL;
        foreach ($tableinfo as $k => $v) {
            $input_type = $this->getFieldType($v);
            $fields .= $this->getFields($input_type, $v);
//            if (in_array($v['column_name'], $this->reservedField)) {
//                continue;
//            }
            $where .= $this->getWhere( $v, $where1);
        }
        if (strlen($where) > 0) {
            $replace_list['where'] = $where;
        }
        $url = ucfirst($controller) . '/' . $controller;
        $replace_list['controller'] = $url;
        if ($add) {
            $replace_list['add'] = '';
        }
        if ($info) {
            $replace_list['info'] = '';
        }
        if ($edit) {
            $replace_list['edit'] = '';
        }
        if ($delect) {
            $replace_list['delete'] = '';
        }
        $replace_list['fields'] = $fields;
        foreach ($replace_list as $k => $item) {
            $temp = str_replace("{%{{$k}}%}", $item, $temp);
        }
        $path = APP_PATH . 'htsoft\view' . DS . $controller . DS . $controller . 'list.html';
        return $this->writeFile($path, $temp);
    }

    /**
     * 创建页面详情
     * @param $table
     * @param $prefix
     * @return bool|int
     */
    function CreateViewinfo($tableinfo, $controller)
    {

        $item = '';//表单控件
        $js = '';//表单控件对应的js
        $val = 'form.val("form1",{';//赋值操作
        $uedit = '';//富文本编辑特殊处理
        foreach ($tableinfo as $k => $v) {
            if ('PRI' == $v['column_key'] || in_array($v['column_name'], $this->reservedField)) {
                if ('PRI' == $v['column_key']) {
                    $item .= '<input type="hidden" name="' . $v['column_name'].
                        '" value="{$info.' . $v['column_name'] . '}">';
                }
                continue;
            }
            $input_type = $this->getFieldType($v);
            $cinfo = $this->getListByComment($v['column_name'], $v['column_comment']);

            $title= $cinfo['field'];
            switch ($input_type) {
                case 'editor':
                    $item .= $this->getItem($input_type, $v,true);
                    $uedit .= "<script> 
                                $(function () {
                                    var  editorcontent = new baidu.editor.ui.Editor({readonly :true});
                                        editorcontent.render( '{$v['column_name']}' );
                                    try{editorcontent.sync();}catch(err){}; 
                                });
                                </script>";
                    break;
                case 'checkbox':
                $item .="<div class=\"layui-form-item mt10\">
                            <label class=\"layui-form-label\">$title</label>
                            <div class=\"layui-input-inline layui-wd2\"><select name=\"$v[column_name]\" xm-select=\"$v[column_name]12\" disabled=\"disabled\"><php>\$$v[column_name]=explode(\",\",\$info['$v[column_name]'])</php>";
                foreach ($cinfo["list"] as $kk=>$vv){
                    $item .= "<option value=\"$kk\"<if condition='in_array(\"$kk\",\$$v[column_name])'>selected=\"selected\"</if>>$vv</option>".PHP_EOL;
                }
                $item .="</select></div></div>";
                    break;
                case 'radio':
                case 'select':
                case 'switch':
                $item .="<div class=\"layui-form-item mt10\">
                            <label class=\"layui-form-label\">$title</label>
                            <div class=\"layui-input-inline layui-wd2\">
                                    <switch name=\"\$info.$v[column_name]\">";
                        foreach ($cinfo["list"] as $kkk=>$vvv){
                            $item .= "<case value='$kkk'><input type=\"text\" class=\"layui-input\"  readonly value=\"$vvv\" ></case>".PHP_EOL;
                        }
                        $item .="</switch></div></div>";
                    break;
                case "id":
                    $ftinfo=$this->dealForeignKeyList([$v])[$v['column_name']];
                    $item .="<div class=\"layui-form-item mt10\">
                            <label class=\"layui-form-label\">$title</label>
                            <div class=\"layui-input-inline layui-wd2\">
                                   <input type=\"text\" class=\"layui-input\"  readonly value=\"{\$info.$ftinfo[table]_$ftinfo[show]}\" >".PHP_EOL;
                    $item .="</div></div>";
                    break;
                default:
                    $val .= PHP_EOL . <<<EOF
                    '{$v['column_name']}': '{\$info.{$v['column_name']}}',
EOF;
                    $item .= $this->getItem($input_type, $v,true);
                    break;
            }
        }
        $js .= rtrim($val, ',') . '});';
        $code = $this->gettemp('info');
        $code = str_replace('{%{item}%}', $item, $code);
        $code = str_replace('{%{uedit}%}', $uedit, $code);
        $code = str_replace('{%{js}%}', $js, $code);
        $path =  APP_PATH . 'htsoft\view'. DS . $controller . DS . $controller . 'info.html';
        return $this->writeFile($path, $code);
    }

    /**
     * 创建添加页面
     * @param $table
     * @param $prefix
     * @return bool|int
     */
    function CreateViewadd($tableinfo, $controller)
    {
        $item = '';//表单控件
        $js = '';//表单控件对应的js
        $uedit = '';//富文本编辑特殊处理
        foreach ($tableinfo as $k => $v) {
            if ('PRI' == $v['column_key'] || in_array($v['column_name'], $this->reservedField)) {
                continue;
            }
            $input_type = $this->getFieldType($v);
            if($input_type=="editor"){
                $uedit .= "<script> 
                                $(function () {
                                    var  editorcontent = new baidu.editor.ui.Editor();
                                        editorcontent.render( '{$v['column_name']}' );
                                    try{editorcontent.sync();}catch(err){}; 
                                });
                                </script>";
            }
            $item .= $this->getItem($input_type, $v);
            $js .= $this->getJs($input_type, $v);
        }
        $url = ucfirst($controller) . '/' . $controller;
        $code = $this->gettemp('add');
        $code = str_replace('{%{item}%}', $item, $code);
        $code = str_replace('{%{uedit}%}', $uedit, $code);
        $code = str_replace('{%{js}%}', $js, $code);
        $code = str_replace('{%{controller}%}', $url, $code);
        $path = APP_PATH . 'htsoft\view' . DS . $controller . DS . $controller . 'add.html';
        return $this->writeFile($path, $code);
    }

    /**
     * 创建编辑页面
     * @param $table
     * @param $prefix
     * @return bool|int
     */
    function CreateViewedit($tableinfo, $controller)
    {
        $item = '';//表单控件
        $js = '';//表单控件对应的js
        $val = 'form.val("form1",{';//赋值操作
        $switch = '';//开关特殊js处理
        $checkbox = '';//复选框特殊js处理
        $uedit = '';//富文本编辑特殊处理
        foreach ($tableinfo as $k => $v) {
            if ('PRI' == $v['column_key'] || in_array($v['column_name'], $this->reservedField)) {
                if ('PRI' == $v['column_key']) {
                    $item .= '<input type="hidden" name="' . $v['column_name'].
                        '" value="{$info.' . $v['column_name'] . '}">';
                }
                continue;
            }

            $input_type = $this->getFieldType($v);
            switch ($input_type) {
                case 'textarea'://文本域不从这里赋值
                    break;
                case 'editor':
                    $uedit .= "<script> 
                                $(function () {
                                    var  editorcontent = new baidu.editor.ui.Editor();
                                        editorcontent.render( '{$v['column_name']}' );
                                    try{editorcontent.sync();}catch(err){}; 
                                });
                                </script>";
                    break;
                case 'radio':
                    $val .= PHP_EOL . <<<EOF
                    '{$v['column_name']}': '{\$info.{$v['column_name']}}',
EOF;
                    break;
                case 'checkbox':
                    $val .= PHP_EOL . <<<EOF
                    '{$v['column_name']}': '{\$info.{$v['column_name']}}',
EOF;
                    $checkbox .= '
                    $("input.' . $v['column_name'] . '").prop("checked",false);
                    let '.$v['column_name'].' = "{$info.' . $v['column_name'] . '}";
                    if('.$v['column_name'].'.length > 0){
                    let st = "{$info.' . $v['column_name'] . '}".split(",");
                      for(let i = 0; i < st.length ;i++){
                        $("input.' . $v['column_name'] . '[value=" + st[i] + "]").prop("checked",true);
                      }
                      form.render("checkbox");
                    }';
                    break;
                case 'switch':
                    $val .= PHP_EOL . <<<EOF
                    '{$v['column_name']}': '{\$info.{$v['column_name']}}',
EOF;
                    //这里有赋值
                    $switch .= <<<TAG
                      $('#{$v['column_name']}').prop('checked','{\$info.{$v['column_name']}}' === '1');
                       form.render('checkbox');
TAG;
                    break;
                case "id":
                    $val .= PHP_EOL . <<<EOF
                    '{$v['column_name']}': '{\$info.{$v['column_name']}}',
EOF;
                    break;
                default:
                    $val .= PHP_EOL . <<<EOF
                    '{$v['column_name']}': '{\$info.{$v['column_name']}}',
EOF;
                    break;
            }
            $item .= $this->getItem($input_type, $v);
            $js .= $this->getJs($input_type, $v);
        }
        $js .= rtrim($val, ',') . '});';
        $js .= $switch;
        $js .= $checkbox;
        $code = $this->gettemp('edit');
        $code = str_replace('{%{item}%}', $item, $code);
        $code = str_replace('{%{uedit}%}', $uedit, $code);
        $code = str_replace('{%{js}%}', $js, $code);
        $url = ucfirst($controller) . '/' . $controller;
        $code = str_replace('{%{controller}%}', $url, $code);
        $path =  APP_PATH . 'htsoft\view' . DS . $controller . DS . $controller . 'edit.html';
        return $this->writeFile($path, $code);
    }

    /**
     *  TODO 控制器名称获取
     */
    function controllername()
    {
        $prefix = C('database.prefix');
        $table = input('table');
        $controller = ucfirst(strtolower(Loader::parseName(preg_replace('/^(' . $prefix . ')/', '', $table), 1)));
        echo json_encode(array('status' => 200, 'data' => $controller));
    }
    /**
     * TODO  数据表信息
     */
    public function tableinfo()
    {
        $table = input('table');
        $tableInfo = Db::query("show full columns from {$table}");
        if ($tableInfo) {
            echo json_encode(array('status' => 1, 'data' => $tableInfo, 'msg' => 'ok'));
        } else {
            echo json_encode(array('status' => 0, 'msg' => '失败'));
        }
    }

    /**
     * todo 写入文件
     * @param $file
     * @param $code
     * @return array
     * @throws Exception
     */
    private function writeFile($file, $code)
    {
        if (file_exists($file)) {
            throw new \think\Exception($file . ' 文件已存在，请删除文件');
        }
        $dir = dirname($file);
        if (!is_dir($dir)) {
            if (!mkdir($dir)) {
                throw new \think\Exception("{$dir} 目录创建失败，权限不足，尝试手动创建");
            }
        }
        //清除结果缓存
        clearstatcache();
        if (!is_writable($dir)) {
            throw new \think\Exception($dir . ' 该目录没有写入权限');
        }
        $fp = fopen($file, 'w');
        $rows = fwrite($fp, $code);
        fclose($fp);
        return ['file' => $file, 'success' => $rows];
    }

    /**
     *  TODO 得到公共模板
     * @param $name string 模板名
     * @return bool|string
     */
    private function gettemp($name)
    {
        return file_get_contents(APP_PATH . 'common' . DS . 'view' . DS . $name . '.html');
    }

    /**
     *  TODO 添加/编辑/查询页各个控件获取
     * @param $type
     * @param $col
     * @return bool|mixed|string
     * @throws Exception
     */
    private function getItem($type, $col,$info=null)
    {
        $replace_list = array(
            'name' => $col['column_name'],
            'title' => '',
            'required1' => ('NO' == $col['is_nullable'] ? '<b style="color: red">* </b>' : ''),
            'required2' => '',
            'attr' => '',
            'text' => '',//开关使用
            'value' => '',//文本域以及富文本编辑器使用
            "readonly"=>''
        );
        if($info){
            $replace_list["readonly"]="readonly";
        }
        switch ($type) {
            case 'number':
                $temp = $this->gettemp('number');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required|number" lay-verType="tips"  ';
                }
                $replace_list['attr'] = ' step="' . 1 / pow(10, $col['numeric_scale'])
                    . '" value="'.$col['column_default'] . '" ';
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'text':
                $temp = $this->gettemp('text');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $replace_list['attr'] = ' maxlength="'.
                    $col['character_maximum_length'] . '" value="' .
                    $col['column_default'] . '" ';
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'color':
                $temp = $this->gettemp('color');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $replace_list['attr'] = ' maxlength="'.
                    $col['character_maximum_length'] . '" value="' .
                    $col['column_default'] . '" ';
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'select':
                $temp = $this->gettemp('select');
                $cinfo = $this->getListByComment($col['column_name'], $col['column_comment']);
                $replace_list['title'] = $cinfo['field'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="msg"  ';
                }
                $options = '';
                foreach ($cinfo['list'] as $k => $v) {
                    $checked = $k == $col['column_default'] ? 'selected' : '';
                    $options .= "<option value={$k} {$checked} >{$v}</option>" . PHP_EOL;
                }
                $replace_list['attr'] = $options;
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'radio':
                $temp = $this->gettemp('radio');
                $cinfo = $this->getListByComment($col['column_name'], $col['column_comment']);
                $replace_list['title'] = $cinfo['field'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="msg"  ';
                }
                $radio = '';
                foreach ($cinfo['list'] as $k => $v) {
                    $checked = $k == $col['column_default'] ? 'checked' : '';
                    $radio .= '<input type="radio" name="' . $col['column_name'] .
                        '" value="' . $k . '" title="' . $v . '" ' . $checked ." ".$replace_list["readonly"].
                        ' >' . PHP_EOL;
                }
                $replace_list['attr'] = $radio;
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'checkbox':
                $temp = $this->gettemp('checkbox');
                $cinfo = $this->getListByComment($col['column_name'], $col['column_comment']);
                $replace_list['title'] = $cinfo['field'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="msg"  ';
                }
                $checkbox = '';
                foreach ($cinfo['list'] as $k => $v) {
                    $checked = $k == $col['column_default'] ? 'checked' : '';
                    $checkbox .= '<input class="' . $col['column_name'] . '"
                     type="checkbox" name="' . $col['column_name'] . '[]" title="' .
                        $v . '" value="' . $k . '" lay-skin="primary"   ' .
                        $checked ." ".$replace_list["readonly"]. ' >' . PHP_EOL;
                }
                $replace_list['attr'] = $checkbox;
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'datetime':
                $temp = $this->gettemp('datetime');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $replace_list['attr'] = '';
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'textarea':
                $temp = $this->gettemp('textarea');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $replace_list['attr'] = '';
                $replace_list['value'] = "{\$info.{$col['column_name']}}";
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'image':
                $temp = $this->gettemp('image');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $replace_list['attr'] = '';
                if($replace_list["readonly"]){
                    $temp="<div class=\"layui-form-item mt10\">
                                <label class=\"layui-form-label\">$replace_list[required1]$replace_list[title]</label>
                                <div class=\"layui-input-inline layui-wd2\">
                                    <img src=\"{\$info.$replace_list[name]}\" style=\"max-height: 150px\" >
                                </div>
                            </div>";
                }else{
                    foreach ($replace_list as $k => $v) {
                        $temp = str_replace("{%{{$k}}%}", $v, $temp);
                    }
                }
                break;
//            case 'file':
//                $temp = $this->gettemp('file');
//                $replace_list['title'] = $col['column_comment'];
//                if (!empty($replace_list['required1'])) {
//                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
//                }
//                $replace_list['attr'] = '';
//                foreach ($replace_list as $k => $v) {
//                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
//                }
//                break;
            case 'switch':
                $temp = $this->gettemp('switch');
                $cinfo = $this->getListByComment($col['column_name'], $col['column_comment']);
                $replace_list['title'] = $cinfo['field'];

                $replace_list['required2'] = 0 == $col['column_default'] ? '' : 'checked';
                $replace_list['attr'] = $col['column_default'];
                $replace_list['text'] = $cinfo['list']['1'] . '|' . $cinfo['list']['0'];
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'editor':
                $temp = $this->gettemp('content');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="msg"  ';
                }
                $replace_list['attr'] = '';
                $replace_list['value'] = "{\$info.{$col['column_name']}|htmlspecialchars_decode}";
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'id':
                $temp = $this->gettemp('select');
                $replace_list['title'] = explode(':',$col['column_comment'])[0];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $info = $this->dealForeignKeyList([$col])[$col['column_name']];
                $replace_list['attr'] = "
                <volist name='{$info['table']}List' id='v'>
                    <option value='{\$v.{$info['pri']}}'>{\$v.{$info['show']}}</option>
                </volist>
                ";
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            default:
                $temp = $this->gettemp('text');
                $replace_list['title'] = $col['column_comment'];
                if (!empty($replace_list['required1'])) {
                    $replace_list['required2'] = ' lay-verify="required" lay-verType="tips"  ';
                }
                $replace_list['attr'] = ' maxlength="' .
                    $col['character_maximum_length'] . '" value="' . $col['column_default'] . '" ';
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
        }
        return $temp;
    }
    /**
     *  TODO 获取数据类型分类
     * @param $v array 数据表列的数据
     * @return string 数据输入类型
     */
    protected function getFieldType(& $v)
    {
        $inputType = 'text';
        switch ($v['data_type']) {
            case 'bigint':
            case 'int':
            case 'mediumint':
            case 'smallint':
            case 'tinyint':
                $inputType = 'number';
                break;
            case 'enum':
            case 'set':
                $inputType = 'select';
                break;
            case 'decimal':
            case 'double':
            case 'float':
                $inputType = 'number';
                break;
            case 'longtext':
            case 'text':
            case 'mediumtext':
            case 'smalltext':
            case 'tinytext':
                $inputType = 'textarea';
                break;
            case 'year':
            case 'date':
            case 'time':
            case 'datetime':
            case 'timestamp':
                $inputType = 'datetime';
                break;
            default:
                break;
        }
        $fieldsName = $v['column_name'];
        // 指定后缀说明也是个时间字段
        if ($this->isMatchSuffix($fieldsName, $this->intDateSuffix)) {
            $inputType = 'datetime';
        }
        // 指定后缀结尾且类型为enum,说明是个单选框
        if ($this->isMatchSuffix($fieldsName, $this->enumRadioSuffix) && $v['data_type'] == 'enum') {
            $inputType = "radio";
        }
        // 指定后缀结尾且类型为set,说明是个复选框
        if ($this->isMatchSuffix($fieldsName, $this->setCheckboxSuffix) && $v['data_type'] == 'set') {
            $inputType = "checkbox";
        }
        // 指定后缀结尾且类型为char或tinyint且长度为1,说明是个Switch复选框
        if ($this->isMatchSuffix($fieldsName, $this->switchSuffix) &&
            ($v['data_type'] == 'tinyint' || $v['column_type'] == 'char(1)') &&
            $v['column_default'] !== '' && $v['column_default'] !== null) {
            $inputType = "switch";
        }

        // 指定后缀结尾且类型为varchar || char,文件上传
        if ($this->isMatchSuffix($fieldsName, $this->imageField) &&
            (($v['data_type'] == 'varchar') || $v['data_type'] == 'char')) {
            $inputType = "image";
        }

        // 指定后缀结尾且类型为varchar || char,文件上传
        if ($this->isMatchSuffix($fieldsName, $this->fileField) &&
            (($v['data_type'] == 'varchar') || $v['data_type'] == 'char')) {
            $inputType = "file";
        }
        //指定后缀结尾 且类型为text系列的字段 为富文本编辑器
        if ($this->isMatchSuffix($fieldsName, $this->editorField) && in_array($v['data_type'], ['longtext', 'mediumtext', 'text', 'smalltext', 'tinytext'])) {
            $inputType = "editor";
        }
        //指定后缀结尾 且类型为input系列的字段 为颜色选择器
        if ($this->isMatchSuffix($fieldsName, $this->colorField) && (($v['data_type'] == 'varchar') || $v['data_type'] == 'char')) {
            $inputType = "color";
        }
        //指定后缀结尾 且类型为number系列的字段 为其他表主键
        if ($this->isMatchSuffix($fieldsName, $this->otherTableId) && (in_array($v['data_type'], ['tinyint','smallint','mediumint','int','bigint']))) {
            $inputType = "id";
        }
        return $inputType;
    }

    /**
     *  TODO 创建视图列表使用 生成layui table控件表格列数据
     * @param $type string 控件类型
     * @param $v array 当前字段详细信息
     * @return string 拼装成的字符串
     * @throws Exception
     */
    private function getFields($type, $v)
    {
        switch ($type) {
            case 'image':
                $fileds = "{field:'{$v['column_name']}',title:'{$v['column_comment']}',align:'center',templet:function(d){
                return '<div><img height=\"50\" src=\"'+d.{$v['column_name']}+'\" onclick=bigimg(\"'+d.{$v['column_name']}+'\") ></div>';
            }},";
                break;
            case 'radio':
            case 'select':
                $width = ",width:110";
                $cinfo = $this->getListByComment($v['column_name'], $v['column_comment']);
                $if = '';
                foreach ($cinfo['list'] as $k => $item) {
                    $if .= "if(d.{$v['column_name']} === '{$k}') return '{$item}';" . PHP_EOL;
                }
                $fileds = "{field:'{$v['column_name']}',title:'{$cinfo['field']}',align:'center',templet:function(d){
                {$if}
            }{$width}},";
                break;
            case 'switch':
                $cinfo = $this->getListByComment($v['column_name'], $v['column_comment']);
                $width = ",width:110";
                $fileds = "{field:'{$v['column_name']}',title:'{$cinfo['field']}',align:'center',templet:function(d){
                    if('1' == d.{$v['column_name']}) return '<div> " . "<span class=\"layui-badge layui-" .
                    "btn-green\">{$cinfo['list']['1']}</span>" . "</div>';
                    " . "if('0' == d.{$v['column_name']}) 
                    return '<div><span class=\"layui-badge\">{$cinfo['list']['0']}</span></div>';
                }{$width}},";
                break;
            case 'checkbox':
                $cinfo = $this->getListByComment($v['column_name'], $v['column_comment']);
                $json = '{';
                foreach ($cinfo['list'] as $k => $v1) {
                    $json .= "'{$k}':'{$v1}',";
                }
                $json = rtrim($json, ',') . '}';
                $if = '
                    let str ="";
                    let st = d.' . $v['column_name'] . '.split(",");
                      let arr = ' . $json . ';
                      for(let i = 0; i < st.length ;i++){
                        if(arr[st[i]]) str += arr[st[i]] + " ";
                      }
                      return str.trim();
                ';
                $fileds = "{field:'{$v['column_name']}',title:'{$cinfo['field']}',align:'center',templet:function(d){
                {$if}
            }},";
                break;
            case 'color':
                $fileds = "{field:'{$v['column_name']}',title:'{$v['column_comment']}',align:'center',templet:function(d){
                    return '<div><input disabled type=\"color\" value=\"'+d.{$v['column_name']}+'\"></div>';
                  }},";
                break;
            case 'id':
                $info = $this->dealForeignKeyList([$v])[$v['column_name']];
                $fileds = "{field:'$info[table]_$info[show]',title:'$info[comment]',align:'center',width:100},";
                break;
            default:
                $width = '';
                if ('PRI' === $v['column_key']) {
                    $width = ",width:80";
                }
                $fileds = "{field:'{$v['column_name']}',title:'{$v['column_comment']}',align:'center'{$width}},";
                break;
        }
        return $fileds;
    }

    /**
     *  TODO 多选、单选、开关的注释解析
     * @param $field string 字段名
     * @param $comment string 字段注释
     * @return array 字段对应的列表
     * @throws Exception 注释格式不正确
     */
    private function getListByComment($field, $comment)
    {
        $res = array();
        $info = explode(':', rtrim($comment, ','));
        if (!(is_array($info) && !empty($info))) {
            throw new \think\Exception('数据表字段 ' . $field . ' 注释格式不正确');
        }

        $res['field'] = $info[0];
        $list = explode(',', $info[1]);
        foreach ($list as $k => $v) {
            $t = explode('=', $v);
            $res['list'][$t[0]] = $t[1];
        }
        return $res;
    }

    /**
     *  TODO 列表页查询控件获取
     * @param $type控件类型
     * @param $col数据库表字段信息
     * @param $where查询条件
     * @return bool|mixed|string
     * @throws Exception
     */
    private function getWhere( $col, $where)
    {
        $temp = '';$type='';
        $replace_list = array(
            'name' => $col['column_name'],
            'title' => '',
            'attr' => '',
        );
//        if ("PRI" === $col['column_key']) {
//            $type = 'pri';
//        }
        if (key_exists($col['column_name'],$where)) {
            $type = $this->getFieldType($col);
            if(!in_array($type,['radio','select','switch',"checkbox","id"])){
                $type = 'pri';
            }
        }
        switch ($type) {
            case 'pri'://pri代表主键
                $temp = $this->gettemp('where_text');
                $replace_list['title'] = $col['column_comment'];
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'radio':
            case 'select':
            case 'switch':
            case 'checkbox':
                $temp = $this->gettemp('where_select');
                $cinfo = $this->getListByComment($col['column_name'], $col['column_comment']);
                $replace_list['title'] = $cinfo['field'];
                $options = '';
                foreach ($cinfo['list'] as $k => $v) {
                    $options .= "<option value='{$k}' >{$v}</option>" . PHP_EOL;
                }
                $replace_list['attr'] = $options;
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            case 'id' :
                $temp = $this->gettemp('where_select');
                $replace_list['title'] = explode(':', $col['column_comment'])[0];
                $info = $this->dealForeignKeyList([$col])[$col['column_name']];
                $options = "
                    <volist name='{$info['table']}List' id='v'>
                    <option value='{\$v.{$info['pri']}}'>{\$v.{$info['show']}}</option>
                    </volist>
                ";
                $replace_list['attr'] = $options;
                foreach ($replace_list as $k => $v) {
                    $temp = str_replace("{%{{$k}}%}", $v, $temp);
                }
                break;
            default:
                break;
        }
        return $temp;
    }

    /**
     *  TODO 判断是否符合指定后缀
     * @param string $field 字段名称
     * @param mixed $suffixArr 后缀
     * @return boolean
     */
    protected function isMatchSuffix($field, $suffixArr)
    {
        $suffixArr = is_array($suffixArr) ? $suffixArr : explode(',', $suffixArr);
        foreach ($suffixArr as $k => $v) {
            if (preg_match("/{$v}$/i", $field)) {
                return true;
            }
        }
        return false;
    }
    /**
     *  TODO javascript获取
     * @param $type
     * @param $col
     * @return bool|mixed|string
     */
    private function getJs($type, $col)
    {
        $js = '';
        $t = $col['data_type'];
        /*        !in_array($t, ['date', 'time', 'year', 'month', 'datetime']) && $t = 'datetime';*/
        switch ($type) {
            case 'datetime':
                $js = "laydate.render({
                          elem: '#{$col['column_name']}'
                          ,theme: '#01AAED'
                          ,type: '{$t}'
                          ,trigger: 'click'
                        });";
                break;
            case 'switch':
                $js = "form.on('switch({$col['column_name']})', function(data){
                            let v = '0';
                            if(data.elem.checked) v = '1';
                            $('input[name={$col['column_name']}]').val(v);
                        }); ";
                break;

            default:
                break;
        }
        return empty($js) ? $js : PHP_EOL . $js . PHP_EOL;
    }
    /**
     *  todo 判断控件类型对应的注释是否有效
     * @param $type
     * @param $filed
     * @param $comment
     * @param $mess string 错误原因
     * @return bool
     */
    protected function judgeComment($type, $filed = '', $comment = '', &$mess)
    {
        $res = true;
        $mess = '';
        switch ($type) {
            case 'radio':
            case 'checkbox':
            case 'select':
                $tmpArr = explode(':', $comment);
                if (2 != count($tmpArr)) {
                    $res = false;
                    $mess = '注释格式有误';
                }
                $tmpArr1 = explode(',' ,trim($tmpArr[1], ','));
                if (count($tmpArr1) < 2) {
                    $res = false;
                    $mess = '注释格式有误';
                }
                $key = array();
                $val = array();
                foreach ($tmpArr1 as $k => $v) {
                    $tmpArr2 = explode('=', $v);
                    if (2 != count($tmpArr2)) {
                        $res = false;
                        $mess .= '<br>键值对不匹配';
                    }
                    if (in_array($tmpArr2[0], $key)) {
                        $res = false;
                        $mess .= '<br> 键重复出现';
                    } else {
                        array_push($key, $tmpArr2[0]);
                    }

                    if (in_array($tmpArr2[1], $val)) {
                        $res = false;
                        $mess .= '<br> 值重复出现';
                    } else {
                        array_push($val, $tmpArr2[1]);
                    }
                }
                break;
            case 'switch':
                $tmpArr = explode(':', $comment);
                if (2 != count($tmpArr)) {
                    $res = false;
                    $mess = '注释格式有误';
                }
                $tmpArr1 = explode(',' ,$tmpArr[1]);
                if (count($tmpArr1) != 2) {
                    $res = false;
                    $mess = '注释格式有误';
                }
                $key = array();
                $val = array();
                foreach ($tmpArr1 as $k => $v) {
                    $tmpArr2 = explode('=', $v);
                    if (2 != count($tmpArr2)) {
                        $res = false;
                        $mess .= '<br> 键值对不匹配';
                    }
                    if (in_array($tmpArr2[0], $key)) {
                        $res = false;
                        $mess .= '<br> 键重复出现';
                    } else {
                        array_push($key, $tmpArr2[0]);
                    }

                    if (in_array($tmpArr2[1], $val)) {
                        $res = false;
                        $mess .= '<br> 值重复出现';
                    } else {
                        array_push($val, $tmpArr2[1]);
                    }

                    if (!in_array($tmpArr2[0], ['0','1'])) {
                        $res = false;
                        $mess .= '<br> switch字段key只能为0或者1';
                    }
                }
                break;
            case 'id':
                $tmpArr = explode('_', $filed);
                array_pop($tmpArr);
                $tName = implode('_',$tmpArr);

                $M = M();
                $sql = "SELECT " . " * FROM `information_schema`.`columns` "
                    . "WHERE TABLE_SCHEMA = '" . C('DB_NAME') . "' AND table_name = '" . C('DB_PREFIX') . $tName . "' "
                    . "ORDER BY ORDINAL_POSITION";
                $tabinfo = $M->query($sql);

                $tmpArr1 = explode(':', $comment);
                if (count($tmpArr1) != 3) {
                    $res = false;
                    $mess = '注释格式有误';
                }

                if (empty($tabinfo)) {
                    $res = false;
                    $mess .= '<br>表'.$tName.'不存在';
                }

                $filedList = array_column($tabinfo, 'column_name');

                //主键列
                if (!in_array($tmpArr1[1],$filedList)) {
                    $res = false;
                    $mess .= '<br>列：'.$tmpArr1[1].'在表'. $tName.'中不存在';
                } else {
                    $k1 = array_search('id', $filedList);
                    if ($tabinfo[$k1]['column_key'] !== 'PRI') {
                        $res = false;
                        $mess .= '<br>列：'.$tmpArr1[1].'在表'. $tName.'中必须是主键';
                    }
                }
                if (!in_array($tmpArr1[2],$filedList)) {
                    $res = false;
                    $mess .= '<br>列：'.$tmpArr1[2].'在表'. $tName.'中不存在';
                }
                break;
            default:
                break;
        }
        if (empty($comment)) {
            $res = false;
            $mess = '注释不可为空';
        }
        $mess .= '<br>注释内容：<b style="color: #dd4444">' .$comment .'</b>';
        return $res;
    }

    /** todo 处理外联表数据
     * @param $list
     * @return array|string
     */
    private function dealForeignKeyList($list)
    {
        if (empty($list)) {
            return '';
        }
        if (count($list) > 7) {
            return '最多8表联查';
        }
        $res = array();
        //最多八表联查
        $alias = ['b','c','d','e','f','g','h'];
        foreach ($list as $k => $v) {
            $tmpArr = explode('_', $v['column_name']);
            array_pop($tmpArr);
            $tName = implode('_',$tmpArr);
            $res[$v['column_name']]['table'] = $tName;
            $res[$v['column_name']]['tableAlias'] = $alias[$k];

            $tmpArr1 = explode(':', $v['column_comment']);
            $res[$v['column_name']]['comment'] = $tmpArr1[0];
            $res[$v['column_name']]['pri'] = $tmpArr1[1];
            $res[$v['column_name']]['show'] = $tmpArr1[2];
            $res[$v['column_name']]['required'] = $v['is_nullable'];
            //外联字段的required 决定使用left 还是inner
        }
        return $res;
    }
}
